import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

/**
 * Created by L.jp
 * Description:你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字： '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转：例如把 '9' 变为 '0'，'0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
 *
 * 锁的初始数字为 '0000' ，一个代表四个拨轮的数字的字符串。
 *
 * 列表 deadends 包含了一组死亡数字，一旦拨轮的数字和列表里的任何一个元素相同，这个锁将会被永久锁定，无法再被旋转。
 *
 * 字符串 target 代表可以解锁的数字，你需要给出解锁需要的最小旋转次数，如果无论如何不能解锁，返回 -1 。
 *
 * 输入：deadends = ["0201","0101","0102","1212","2002"], target = "0202"
 *
 * 输出：6
 *
 * 解释：
 * 可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
 * 注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的，
 * 因为当拨动到 "0102" 时这个锁就会被锁定。

 * User: 86189
 * Date: 2021-11-30
 * Time: 23:14
 */
//朴素bfs
public class TurntableLock {
    public static int openLock(String[] deadends, String target) {
        Set<String> deadSet=new HashSet<>();//存放死亡数字，用于查找，哈希表查询速率最高
        for(String dead:deadends){
            deadSet.add(dead);
        }
        if(deadSet.contains("0000")){
            return -1;
        }
        Set<String> visitedSet=new HashSet<>();
        visitedSet.add("0000");
        int minStep=0;//因为最初的0000不算在最小旋转次数当中
        Queue<String>  tmpQue=new LinkedList<>();
        tmpQue.offer("0000");
        while(!tmpQue.isEmpty()){
            int size=tmpQue.size();
            while(size--!=0){
                String curStr=tmpQue.poll();
                //如果弹出的字符串等于目标字符串，那么就直接返回步数
                if(curStr.equals(target)){
                    return minStep;
                }
                //每一位都要变,一次变换一位
                for(int i=0;i<curStr.length();i++){
                    //那么怎么实现数字的改变呢？就是先得到每个字符，然后利用字符拼接函数StringBuilder实现每个字符的替换
                    // 每一个字符都有两种情况转变，可以向上转变大，也可以向下转变小
                    char chUp=curStr.charAt(i);//用于向上改变的字符
                    char chDn=curStr.charAt(i);//用于向下转变的字符
                    //如果向上转时，当前字符是9，那么下一步就变成了0，这里的赋值就表示走下一步，每次只能走一步
                    if(chUp=='9') {
                        chUp = '0';
                    }
                    else {
                        chUp++;
                    }
                    //如果向下转时，当前字符是0，那么下一步就变成了9，
                    if(chDn=='0') {
                        chDn = '9';
                    }
                    else{
                        chDn--;
                    }
                    //修改字符，需要用两个字符串拼接函数改变向上的，和向下的
                    StringBuilder sbUP=new StringBuilder(curStr);
                    StringBuilder sbDn=new StringBuilder(curStr);
                    sbUP.setCharAt(i,chUp);
                    sbDn.setCharAt(i,chDn);
                    //判断修改后的字符是否包含在死亡数字哈希表中和是否被访问过
                    if(!deadSet.contains(sbUP.toString()) && !visitedSet.contains(sbUP.toString())){
                        tmpQue.offer(sbUP.toString());
                        visitedSet.add(sbUP.toString());
                    }
                    if(!deadSet.contains(sbDn.toString()) && !visitedSet.contains(sbDn.toString())){
                        tmpQue.offer(sbDn.toString());
                        visitedSet.add(sbDn.toString());
                    }

                }
            }
            minStep++;//
        }
        return -1;//其他的情况不符合的，直接返回-1
    }

    public static void main(String[] args) {
        String[] str={"0201","0101","0102","1212","2002"};
        String target="0202";
        System.out.println(openLock(str, target));
    }
}
